﻿using System;
using System.Threading.Tasks;
using System.Collections.Generic;

using MyCtp;
using MyCtp.Model;

namespace MyCtp.Api
{
    /// <summary>
    /// 行情数据接口操作类
    /// </summary>
    public class MdApi
    {
        /// <summary>
        /// 初始化并异步运行行情数据接口
        /// </summary>
        /// <param name="brokerId">经纪商ID</param>
        /// <param name="userId">账户</param>
        /// <param name="pwd">密码</param>
        /// <param name="frontAddresses">前置机地址（可多个）</param>
        /// <param name="instruments">要订阅行情的合约代码</param>
        public MdApi(string brokerId, string userId, string pwd, string[] frontAddresses, string[] instruments)
        {
            brokerID = brokerId;
            userID   = userId;
            password = pwd;

            if (instruments != null)
            {
                subscribedInstruments = new string[instruments.Length];
                instruments.CopyTo(subscribedInstruments, 0);
            }

            Init(frontAddresses);
        }

        /// <summary>
        /// 获取行情数据队列中的数据（先进先出）
        /// </summary>
        /// <returns>深度行情数据结构（没有数据则返回null）</returns>
        public CThostFtdcDepthMarketDataField GetDepthMarketData()
        {
            return (marketDataQueue.Count > 0) ? marketDataQueue.Dequeue() : null;
        }

        
        /// <summary>
        /// 最新价格到达事件代理
        /// </summary>
        /// <param name="code">代码</param>
        /// <param name="price">最新价格</param>
        /// <param name="updateTime">更新时间</param>
        public delegate void LastPriceDelegate(string code, double price, DateTime updateTime);
        
        /// <summary>
        /// 最新价格到达事件（异步调用）
        /// </summary>
        public event LastPriceDelegate LastPriceArrive;
        

        /// <summary>
        /// 重新订阅行情
        /// </summary>
        /// <param name="instruments">订阅行情的合约列表</param>
        public void ReSubscribeMarketData(string[] instruments)
        {
            //只有登录成功后才能订阅行情
            if (isLogin)
            {
                //取消先前订阅的行情
                if (subscribedInstruments != null)
                    ctpMdApi.UnSubscribeMarketData(subscribedInstruments, subscribedInstruments.Length);

                if (instruments != null)
                {
                    //取得新的订阅行情列表
                    subscribedInstruments = new string[instruments.Length];
                    instruments.CopyTo(subscribedInstruments, 0);

                    //订阅新的行情列表
                    ctpMdApi.SubscribeMarketData(subscribedInstruments, subscribedInstruments.Length);
                }
            }
        }

        /// <summary>
        /// 释放行情接口对象
        /// </summary>
        public void Release()
        {
            ctpMdApi.Release();
        }


        /**************************** Private ****************************/

        /// <summary>
        /// 行情数据接口对象
        /// </summary>
        private MyCtp.CtpMdApi ctpMdApi;

        /// <summary>
        /// 经纪商ID
        /// </summary>
        private string brokerID;

        /// <summary>
        /// 账户
        /// </summary>
        private string userID;

        /// <summary>
        /// 密码
        /// </summary>
        private string password;

        /// <summary>
        /// 请求序号
        /// </summary>
        private int requestID = 0;

        /// <summary>
        /// 是否已经登录成功
        /// </summary>
        private bool isLogin = false;

        /// <summary>
        /// 订阅行情的合约列表
        /// </summary>
        private string[] subscribedInstruments;

        /// <summary>
        /// 行情数据队列
        /// </summary>
        private Queue<CThostFtdcDepthMarketDataField> marketDataQueue;


        /// <summary>
        /// 初始化并运行行情数据接口
        /// </summary>
        private void Init(string[] fronts)
        {
            //生成行情数据队列
            marketDataQueue = new Queue<CThostFtdcDepthMarketDataField>(100);

            //初始化接口对象并注册前置机地址
            ctpMdApi = new CtpMdApi();

            //注册前置机地址
            foreach (string front in fronts)
                ctpMdApi.RegisterFront(front);

            //注册接口回调事件
            ctpMdApi.CtpMdEvents.OnFrontConnected      += this.OnFrontConnected;
            ctpMdApi.CtpMdEvents.OnRspUserLogin        += this.OnRspUserLogin;
            ctpMdApi.CtpMdEvents.OnRspSubMarketData    += this.OnRspSubMarketData;
            ctpMdApi.CtpMdEvents.OnRtnDepthMarketData  += this.OnRtnDepthMarketData;

            //初始化并运行
            ctpMdApi.Init();
            Task.Factory.StartNew(ctpMdApi.Join);
        }

        /// <summary>
        /// 连接前置机并登录
        /// </summary>
        private void OnFrontConnected()
        {
            CThostFtdcReqUserLoginField loginField = new CThostFtdcReqUserLoginField();

            loginField.BrokerID = brokerID;
            loginField.UserID   = userID;
            loginField.Password = password;

            ctpMdApi.ReqUserLogin(loginField, requestID++);
        }

        /// <summary>
        /// 用户登录应答
        /// </summary>
        private void OnRspUserLogin(CThostFtdcRspUserLoginField rspUserLoginfield, CThostFtdcRspInfoField rspInfoField, int nRequestID, bool bIsLast)
        {
            //登录成功则订阅行情
            if (rspInfoField.ErrorID == 0 && subscribedInstruments != null)
            {
                ctpMdApi.SubscribeMarketData(subscribedInstruments, subscribedInstruments.Length);
            }
        }

        private void OnRspSubMarketData(CThostFtdcSpecificInstrumentField pSpecificInstrument, CThostFtdcRspInfoField pRspInfo, int nRequestID, bool bIsLast)
        {
            
        }

        /// <summary>
        /// 发布最新行情数据
        /// </summary>
        /// <param name="pDepthMarketData">深度行情数据结构</param>
        private void OnRtnDepthMarketData(CThostFtdcDepthMarketDataField pDepthMarketData)
        {
            if (pDepthMarketData != null)
            {
                LastPriceArrive(pDepthMarketData.InstrumentID, pDepthMarketData.LastPrice, DateTime.Parse(pDepthMarketData.UpdateTime));
            }
        }
    }
}
